use windows_sys::{
    Win32::Foundation::*, Win32::System::SystemServices::*, Win32::System::Threading::*,
    Win32::UI::WindowsAndMessaging::*, core::*,
};

use std::{
    io::{Error, ErrorKind, Result},
    mem::MaybeUninit,
};

pub fn find_window_by_pid(pid: u32) -> Result<HWND> {
    let mut data = EnumWindowsData {
        wanted_pid: pid,
        handle: HWND::default(),
        found: false,
    };
    unsafe {
        EnumWindows(
            Some(enum_windows_callback),
            &mut data as *mut EnumWindowsData as isize,
        );
    };
    if !data.found {
        return Err(Error::new(ErrorKind::NotFound, "Can't find the window!"));
    }
    Ok(data.handle)
}

#[derive(Default)]
struct EnumWindowsData {
    wanted_pid: u32,
    handle: HWND,
    found: bool,
}

unsafe extern "system" fn enum_windows_callback(handle: HWND, lparam: LPARAM) -> BOOL {
    let data = lparam as *mut EnumWindowsData;
    let mut data = data.as_mut().unwrap();

    let mut pid = MaybeUninit::<u32>::zeroed();
    GetWindowThreadProcessId(handle, pid.as_mut_ptr());
    let pid = pid.assume_init();

    if pid == data.wanted_pid
        && GetWindow(handle, GW_OWNER).is_null()
        && IsWindowVisible(handle) == 1
    {
        data.handle = handle;
        data.found = true;
        return 0;
    }

    1
}
